/**
 * 格子对象
 */
class Grid {
  constructor({
    index,
    row,
    col,
    ele,
    mineNum = 0,
    isMine = false,
    isMark = false,
    isOpen = false,
  }) {
    this.ele = ele;
    this.GridIndex = index;
    this.mineNum = mineNum;
    this.isMine = isMine;
    this.isOpen = isOpen;
    this.isMark = isMark;
    this.row = row;
    this.col = col;
  }
}
/**
 *@param { }
 * 游戏对象
 *
 */
class Game {
  constructor({ target, level }) {
    if (!target) {
      throw "target is required;An entry element must be bound";
    }
    if (!level) {
      throw "level is required;Please select the difficulty";
    }
    /*    super(props) */
    this.level = level;
    this.target = document.querySelector(target);
    this.gridsRow = [];
    this.gridsCol = [];
    this.minePoistion = [];
    this.type = "open";
    this.markMine = 0;
    this.markNum = 0;
    this.oMain = null;
    this.oSpan = [];
    this.startTime = 0;
    this.timer = null;
    this.domTree = [
      {
        target: "div",
        className: ["game-header"],
        child: [
          {
            target: "span",
            className: ["game-label", "game-start"],
            text: "开始游戏",
            event: () => {
              this.init();
            },
          },
          {
            target: "span",
            className: ["game-label", "game-mark"],
            text: "标记/取消",
            event: () => {
              this.gridMark();
            },
          },
          {
            target: "span",
            className: ["game-label", "game-open"],
            text: "打开",
            event: () => {
              this.gridOpen();
            },
          },
          {
            target: "span",
            className: ["game-label", "game-mark-num"],
          },
          {
            target: "span",
            className: ["game-label", "game-time"],
          },
        ],
      },
      {
        target: "div",
        className: "game-main",
      },
    ];
    this.drawHtml();
  }
  init() {
    this.getMinePosition();
    this.drawPlane();
    this.oSpan[2].classList.add("active");
    this.oSpan[3].innerHTML = this.getMarkNum();
    this.oSpan[4].innerHTML = "00:00:00";
    this.startTime = new Date().getTime();
    this.reckonByTime()
  }
  reckonByTime() {
    if(this.timer){
      clearInterval(this.timer)
    }
    this.timer = setInterval(() => {
      const time = new Date().getTime() - this.startTime;
      this.oSpan[4].innerHTML = this.showTime(time);
    }, 1000);
  }
  showTime(time) {
    let hour = Math.floor(time / (3600 * 1000));
    let min = Math.floor(time / 1000 / 60 % 60);
    let second = Math.floor(time / 1000 % 60);
    console.log(hour,min,second)
    hour = hour < 10 ? `0${hour}` : hour;
    min = min < 10 ? `0${min}` : min;
    second = second < 10 ? `0${second}` : second;
    return `${hour}:${min}:${second}`
  }
  drawHtml() {
    //
    // const mineGame  = document.createElement('div').classList.add('game')
    this.target.classList.add("game-mine");
    const htmlF = document.createDocumentFragment();
    this.domTree.forEach((ele) => {
      const target = document.createElement(ele.target);
      target.classList.add(ele.className[0]);

      if (ele.child && ele.child.length > 0) {
        ele.child.forEach((cEle) => {
          const cTarget = document.createElement(cEle.target);

          cTarget.classList.add(cEle.className[0]);
          cTarget.classList.add(cEle.className[1]);

          console.log(cEle.className.length);
          if (cEle.text) {
            cTarget.innerHTML = cEle.text;
          }
          if (cEle.event) {
            cTarget.onclick = cEle.event;
          }

          this.oSpan.push(cTarget);
          target.appendChild(cTarget);
        });
      }
      if (ele.className.includes("game-main")) {
        this.oMain = target;
      }
      htmlF.appendChild(target);
    });

    this.target.appendChild(htmlF);
  }
  /**
   * 根据 行，列生成二维数组
   * @returns
   */
  getGridsList() {
    const { row, col } = this.level;
    let result = [];

    for (let i = 0; i < row; i++) {
      result[i] = [];
      for (let j = 0; j < col; j++) {
        result[i].push(
          new Grid({
            index: this.getGridIndex({ x: i, y: j, len: row }),
            row: i,
            col: j,
          })
        );
      }
    }
    return result;
  }
  /**
   * 生成索引
   * @param {Object} coordinate
   * @returns
   */
  getGridIndex({ x, y, len } = coordinate) {
    return x * len + y;
  }
  /**
   * 绘画格子
   */
  drawPlane() {
    this.oMain.innerHTML = "";
    const { row, col } = this.level;
    const result = this.getGridsList();
    const fragment = document.createDocumentFragment();

    result.forEach((item) => {
      let oRow = document.createElement("div");
      item.forEach((cItem) => {
        const oCol = document.createElement("div");
        cItem.ele = oCol;
        oCol.classList.add("game-col");
        oCol.classList.add("game-col-grid");
        if (this.minePoistion.includes(cItem.GridIndex)) {
          oCol.classList.add("game-mine");
          cItem.isMine = true;
        } else {
          cItem.mineNum = this.getMineNub(cItem);
          oCol.classList.add(`game-col-${cItem.mineNum}`);
        }

        oCol.onclick = () => {
          this.handleClick(oCol, cItem);
        };
        this.gridsCol.push(cItem);
        oRow.appendChild(oCol);
      });
      oRow.classList.add("game-row");
      fragment.appendChild(oRow);
    });
    this.oMain.appendChild(fragment);
  }
  handleOpen(event, info) {
    let num = 0;
    const { GridIndex, col: y, row: x } = info;
    const { row } = this.level;
    this.getCoordinate([x, y], row);
  }
  /**
   *
   * @param {*} type open(默认打开) || mark（标记） || unmark（取消标记）
   */
  gridOpen() {
    this.oSpan[1].classList.remove("active");
    this.oSpan[2].classList.add("active");
    this.type = "open";
  }

  gridMark(type) {
    this.oSpan[2].classList.remove("active");
    this.oSpan[1].classList.add("active");
    this.type = "mark";
  }

  /**
   * 格子点击事件
   * @param {*} ele  格子元素
   * @param {*} grid  格子对象
   */
  handleClick(ele, grid) {
    if (this.type == "open") {
      this.setGridOpen(ele, grid);
    } else {
      this.setGridMark(ele, grid);
    }
  }
  setGridOpen(ele, grid) {
    //ele.innerHTML = grid.isMine ? "*" : grid.mineNum;
    if (grid.isMine) {
      this.changeGrid(this.gridsCol[grid.GridIndex], "*");
      this.gridsCol[grid.GridIndex].isMine = true;
      alert("game over");
      clearInterval(this.timer)
    } else if (grid.mineNum && !grid.isMark) {
      this.changeGrid(this.gridsCol[grid.GridIndex], grid.mineNum);
    } else if (grid.mineNum === 0) {
      this.setRoundIsOpen(ele, grid);
    }
  }
  setGridMark(ele, grid) {
    if (!grid.isOpen) {
      if (!grid.isMark) {
        if (this.markNum < this.level.mine) {
          grid.isMark = true;
          grid.ele.classList.add("game-mark");
          this.markNum++;
        }
      } else {
        grid.isMark = false;
        grid.ele.classList.remove("game-mark");
        this.markNum--;
      }
    }

    this.oSpan[3].innerHTML = this.getMarkNum();
    console.log(this.getMarkNum());
    if (this.markMine === this.level.mine) {
      alert("游戏胜利");
      clearInterval(this.timer)
    }
  }
  getMarkNum() {
    let num = 0;
    this.markMine = 0;
    this.gridsCol.forEach((item) => {
      if (item.isMark) {
        num++;
      }
      if (item.isMark && item.isMine) {
        this.markMine++;
      }
    });
    return this.level.mine - num;
  }
  changeGrid(ele, content) {
    ele.ele.innerHTML = content;
    ele.isOpen = true;
    ele.ele.classList.remove("game-col-grid");
    ele.onclick = "";
  }
  /**
   * 输入一个坐标（x,y）获取以自身为中心的九宫格坐标
   *  要求：1、所以x,y坐标都不小于0
   *        2、所有坐标小于 n-1
   */
  getCoordinate(coordinate, len) {
    let x = coordinate[0];
    let y = coordinate[1];
    let arr = [];
    for (let i = x - 1; i <= x + 1; i++) {
      for (let j = y - 1; j <= y + 1; j++) {
        arr.push({
          x: i,
          y: j,
        });
      }
    }
    const filterArr = arr.filter(
      (cur) => 0 <= cur.x && cur.x < len && 0 <= cur.y && cur.y < len
    );
    const IndexArr = [];
    filterArr.forEach((item) => {
      const { x, y } = item;
      IndexArr.push(
        this.getGridIndex({
          x,
          y,
          len,
        })
      );
    });
    return IndexArr;
  }
  /**
   *获取格子周围雷数
   * @param {*} info 格子对象
   * @returns  返回格子周围雷数量
   */
  getMineNub(info) {
    const { row, col } = info;
    const gridIndexArr = this.getCoordinate([row, col], level.row);
    let num = 0;
    gridIndexArr.forEach((item) => {
      if (this.minePoistion.includes(item)) {
        num++;
      }
    });
    return num;
  }
  /**
   * 生成雷的位置索引数组
   */
  async getMinePosition() {
    const { col, row, mine } = this.level;
    const maxNum = col * row;
    let result = [];
    for (let i = 0; i < mine; i++) {
      let random = Math.floor(Math.random() * (maxNum - 0 + 1) + 0);
      if (result.includes(random)) {
        i--;
      } else {
        result.push(random);
      }
    }
    this.minePoistion = result;
  }

  setRoundIsOpen(ele, grid) {
    if (this.getCoordinate([grid.row, grid.col], level.row).length > 0) {
      this.fun(this.getCoordinate([grid.row, grid.col], level.row), grid);
    }
  }
  fun(arr, grid) {
    for (let i = 0; i < arr.length; i++) {
      if (
        !this.gridsCol[arr[i]].isMine &&
        !this.gridsCol[arr[i]].isOpen &&
        !this.gridsCol[arr[i]].isMark
      ) {
        this.changeGrid(this.gridsCol[arr[i]], this.gridsCol[arr[i]].mineNum);
        if (this.gridsCol[arr[i]].mineNum === 0) {
          this.changeGrid(this.gridsCol[arr[i]], this.gridsCol[arr[i]].mineNum);
          this.setRoundIsOpen(this.gridsCol[arr[i]].ele, this.gridsCol[arr[i]]);
        }
      }
    }
  }
}

